package com.common.filter;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ValidateException;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTValidator;
import com.common.authentication.Oauth2ModeAuthentication;
import com.common.exception.AccessTokeFormatAuthenticationException;
import com.common.exception.AccessTokenEmptyAuthenticationException;
import com.common.exception.AccessTokenExpiredAuthenticationException;
import com.common.handler.SystemDefaultAuthenticationFailureHandler;
import com.common.matcher.SystemDefaultAuthenticationRequestMatcher;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

/**
 * @Description: 系统默认的身份验证过滤器
 * @Date: 2025/6/6 14:35
 */
@Component
public class SystemDefaultAuthenticationFilter extends OncePerRequestFilter {

    @Autowired(required = false)
    private RequestMatcher requiresAuthenticationRequestMatcher = new SystemDefaultAuthenticationRequestMatcher();

    @Autowired(required = false)
    private AuthenticationFailureHandler failureHandler = new SystemDefaultAuthenticationFailureHandler();

    private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository();

    private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
            .getContextHolderStrategy();

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //判断请求头是否包含Authorization，存在该值则过滤器继续执行
        if (requiresAuthenticationRequestMatcher.matches(request)) {
            try {
                String accessToken = request.getHeader(HttpHeaders.AUTHORIZATION);
                if (ObjectUtils.isEmpty(accessToken)) {
                    throw new AccessTokenEmptyAuthenticationException("认证失败,Authorization为空");
                }
                String[] accessTokenArray = accessToken.split(" ");
                if (accessTokenArray.length != 2) {
                    throw new AccessTokeFormatAuthenticationException("认证失败,Authorization格式不正确");
                }
                String token = accessTokenArray[1];
                try {
                    JWTValidator.of(token).validateDate(DateUtil.date());
                }catch (RuntimeException validateException){
                    throw new AccessTokenExpiredAuthenticationException("认证失败,Authorization已过期");
                }
                JWT jwt = JWT.of(token);
                Object userId = jwt.getPayload("userId");
                Authentication authentication = new Oauth2ModeAuthentication(userId);
                authentication.setAuthenticated(true);
                SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
                context.setAuthentication(authentication);
                this.securityContextHolderStrategy.setContext(context);
                this.securityContextRepository.saveContext(context, request, response);
            } catch (AuthenticationException e) {
                failureHandler.onAuthenticationFailure(request, response, e);
            }
        }
        filterChain.doFilter(request, response);
    }

}
